package com.corticon.dialogs.validation;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

import com.corticon.dialogs.dao.MessageDAO;

/**
 * Convenience class for resolving/formatting Spring Validation <code>Errors</code> into <code>String</code>
 * messages using an underlying <code>MessageSource</code>. Due to usage of a
 * <code>MessageSource</code> bundled, localized, and internationalized
 * messages are supported.
 *
 * @author Yanglifan
 */
public class ValidationMessageFormatter {

    private String fieldErrorMsgKeySuffix = ".errors";
    private MessageSource messageSource;
    @Resource
    private MessageDAO messageDAO;

    /**
     * Get the first <code>FieldError</code> validation message
     * for the given fieldName.
     *
     * @param errors The validation <code>Errors<code>.
     * @param fieldName The fieldName to retrieve the error message for.
     * @return The <code>String</code> validation message or null.
     */
    public String getFieldErrorMessage(Errors errors, String fieldName) {
        Locale locale = LocaleContextHolder.getLocale();
        return getFieldErrorMessage(errors, fieldName, null, locale);
    }

    /**
     * Get the first <code>FieldError</code> validation message
     * for the given fieldName.
     *
     * @param errors The validation <code>Errors<code>.
     * @param fieldName The fieldName to retrieve the error message for.
     * @param locale The desired <code>Locale</code> of the message.
     * @return The <code>String</code> validation message or null.
     */
    public String getFieldErrorMessage(Errors errors, String fieldName, Locale locale) {
        return getFieldErrorMessage(errors, fieldName, null, locale);
    }

    /**
     * Get the first <code>FieldError</code> validation message
     * for the given fieldName.
     *
     * @param errors The validation <code>Errors<code>.
     * @param fieldName The fieldName to retrieve the error message for.
     * @param args Arguments to be populated within the validation message.
     * @return The <code>String</code> validation message or null.
     */
    public String getFieldErrorMessage(Errors errors, String fieldName, Object[] args) {
        Locale locale = LocaleContextHolder.getLocale();
        return getFieldErrorMessage(errors, fieldName, args, locale);
    }

    /**
     * Get the first <code>FieldError</code> validation message
     * for the given fieldName.
     *
     * @param errors The validation <code>Errors<code>.
     * @param fieldName The fieldName to retrieve the error message for.
     * @param args Arguments to be populated within the validation message.
     * @param locale The desired <code>Locale</code> of the message.
     * @return The <code>String</code> validation message or null.
     */
    public String getFieldErrorMessage(Errors errors, String fieldName, Object[] args, Locale locale) {
        String message = null;

        FieldError fieldError = errors.getFieldError(fieldName);
        if (fieldError != null) {
            String codes[] = fieldError.getCodes();
            for (String code : codes) {
                message = messageSource.getMessage(code, args, null, locale);

                if (message != null) {
                    // A message matching the code has been found
                    break;
                }
            }
            if (message == null) {
                message = fieldError.getDefaultMessage();
            }
        }

        return message;
    }

    /**
     * This method converts the error messages from <code>org.springframework.validation.Errors</code>
     * to java.util.Map.
     * @param validateObject
     * @param errors
     * @param locale
     * @return
     */
    public Map<String, List<String>> convertErrorsMessageToMap(Object validateObject, Errors errors,
        Locale locale) {
        Map<String, List<String>> errorMessages = new HashMap<String, List<String>>();

        // Get all fields of the validate object.
        Field[] fields = validateObject.getClass().getDeclaredFields();
        Field[] superFields = validateObject.getClass().getSuperclass().getDeclaredFields();
        Field[] allFields = (Field[]) ArrayUtils.addAll(fields, superFields);

        for (Field field : allFields) {
            List<String> fieldErrorMessages = new ArrayList<String>();

            // Get all field errors according to the field name.
            List fieldErrorList = errors.getFieldErrors(field.getName());
            for (int i = 0; i < fieldErrorList.size(); i++) {
            				/**
            		 *  modify by heweiya
            				 *    old way:message from spring local langage
            				 *    new way:message from database  
            				 */
                FieldError fieldError = (FieldError) fieldErrorList.get(i);
                String fieldErrorMessage = "";
                /**    old way:
                 * fieldErrorMessage = messageSource.getMessage(fieldError.getCode(),
                    fieldError.getArguments(), fieldError.getDefaultMessage(), locale);
                    		*/
                fieldErrorMessage = messageDAO.getMessage(field.getName(),fieldError.getCode(), fieldError.getDefaultMessage(), locale.getLanguage());
                fieldErrorMessages.add(fieldErrorMessage);
            }
            errorMessages.put(field.getName() + fieldErrorMsgKeySuffix, fieldErrorMessages);
        }

        return errorMessages;
    }

    public void setFieldErrorMsgKeySuffix(String fieldErrorMsgKeySuffix) {
        this.fieldErrorMsgKeySuffix = fieldErrorMsgKeySuffix;
    }

    /**
     * Set the <code>MessageSource</code>.
     *
     * @param messageSource The <code>MessageSource</code>.
     */
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
}